package modele;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;

import modele.WorldElem.Burning;
import modele.WorldElem.Flooded;
import modele.WorldElem.Grass;
import modele.WorldElem.None;
import modele.WorldElem.Rock;
import modele.WorldElem.Sand;
import modele.WorldElem.SecondaryState;
import modele.WorldElem.State;
import modele.WorldElem.Tree;
import modele.WorldElem.WaterSource;
import modele.worldAgent.Agent;
import modele.worldAgent.Patou;
import modele.worldAgent.Sheep;
import modele.worldAgent.Wolf;

import utility.Perlin;
import controller.Event;
import controller.FirePropagation;
import controller.GlobalEvolving;
import controller.NaturalEvolution;
import controller.Rule;
import controller.WaterFlow;
import controller.WaterFlowRule1;
public class World{
	private Square[][] squareMap;
	private Agent[][] agentsMap;
	private ArrayList<Event> eventList;
	private ArrayList<Square> toRepaint;
	private int width;
	private ArrayList<Agent> agents;
	public final int NBWOLF = 150;
	public final int NBSHEEP = 1000;
	public final int NBPATOU = 80;
	
	int nbWolf;
	int nbSheep;
	int nbPatou;
	int tour;
	
	public Agent[][] getAgentsMap() {
		return agentsMap;
	}



	public ArrayList<Agent> getAgents() {
		return agents;
	}


	public void setAgents(ArrayList<Agent> agents) {
		this.agents = agents;
	}


	public World(){
		squareMap = null;
		eventList = null;
		toRepaint = null;
		width = 0;
	}
	
	
	public World(int width){
		toRepaint = new ArrayList<Square>();
		this.width = width;
		float[][] map=new float[width][width];
		float[][] distmap = new float[width][width];
		eventList =  new ArrayList<Event>();
//		eventList.add(new FirePropagation(this));
//		eventList.add(new WaterFlow(this));
//		eventList.add(new NaturalEvolution(this));
		eventList.add(new GlobalEvolving(this));
		tour = 0;
		
		map=Perlin.GeneratePerlinNoise(Perlin.GenerateWhiteNoise(width, width),6 );
		distmap=map;
		squareMap = new Square[width][width];
		float limitSand = 55.0f;
		float limitWater = 50.0f;
		float limitVegetation = 150.0f;
		Random rg = new Random();
		int nbWaterSource = 2;
		ArrayList<Square> rocks = new ArrayList<Square>();
		for(int i=0; i<width; i++){
			for(int j =0;j<width;j++){
				//limitSand = 55.0f+(rg.nextFloat()-0.5f)*20;
				limitWater = 50.0f+(rg.nextFloat()-0.5f)*20;
				limitVegetation = 150.0f+(rg.nextFloat()-0.5f)*20;
				State s = null;
				if(distmap[i][j]*255<limitSand){
					s = new Sand();
				}

				if( distmap[i][j]*255 >=limitSand && distmap[i][j]*255<limitVegetation){
					Random r= new Random();
					if(r.nextFloat()*distmap[i][j]*255<70)
					s = new Tree();
					else s=new Grass();
					((None) s.getSecondaryState()).setNoneTime(r.nextInt(Grass.TOTREE/10)+1);
				}
				if( distmap[i][j]*255 >limitVegetation){
					s = new Rock();
					
					
				}
				if((int) (map[i][j]*255) < (int) limitWater){
					s.setSecondaryState(new Flooded());
					((Flooded)(s.getSecondaryState())).setWaterLevel((int)( limitWater) - (int)(map[i][j]*255));
					
				}
				
				squareMap[i][j] = new Square((int) (map[i][j]*254), i, j, s);
				if(squareMap[i][j].getStateValue()== State.ROCK){
					rocks.add(squareMap[i][j]);
				}
			}
		}
		
		int limitWaterSource = (rocks.size()<nbWaterSource)? rocks.size() : nbWaterSource;
			
		for(int k = 0 ; k < limitWaterSource ; k++){
			Random r = new Random();
			int index = r.nextInt(rocks.size());
			rocks.get(index).setState(new WaterSource());
			rocks.remove(index);
		}
		
//		for(int i = 0 ; i < width ; i++){
//			for(int j = 0 ; j < width ; j++){
//				if(squareMap[i][j].getSecondaryStateValue() == SecondaryState.FLOODED){
//					Iterator<ArrayList<Square>> itA = squareMap[i][j].getSquareAround(this , 1).iterator();
//					while(itA.hasNext()){
//						Iterator<Square> itS = itA.next().iterator();
//						while(itS.hasNext()){
//							Square tempSq = itS.next();
//							SecondaryState ss = tempSq.getSecondaryState();
//							State tempSt = new Sand();
//							tempSt.setSecondaryState(ss);
//							tempSq.setState(tempSt);
//						}
//					}
//				}
//				toRepaint.add(squareMap[i][j]);
//			}
//		}
//		SecondaryState ss = new Burning();
//		State s = new Grass(ss);
//		squareMap[3][0]  = new Square(254, 3, 0, s);
	
		Random r = new Random();
		int x = r.nextInt(width);
		int y = r.nextInt(width);
		int i = 0 ;
		agentsMap = new Agent[width][width]; 
		while(i < NBSHEEP){
			if(agentsMap[x][y] == null){
				addAgent(new Sheep(x, y, this));
				nbSheep++;
				i++;
			}
			x = r.nextInt(width);
			y = r.nextInt(width);
		}
		i = 0;
		while(i < NBWOLF){
			if(agentsMap[x][y] == null){
				addAgent(new Wolf(x, y, this));
				nbWolf++;
				i++;
			}
			x = r.nextInt(width);
			y = r.nextInt(width);
		}
		i = 0;
		while(i < NBPATOU){
			if(agentsMap[x][y] == null){
				addAgent(new Patou(x, y, this));
				nbPatou++;
				i++;
			}
			x = r.nextInt(width);
			y = r.nextInt(width);
		}
//		agents = new ArrayList<Agent>();
//		agents.add(new Sheep(0,0,this));
	}
	
	
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		String ret = new String();
		ret= "World map :\n";
		for(int i=0; i<width; i++){
			for(int j =0;j<width;j++){
				ret+=squareMap[i][j].getPosZ()+" ";
			}
			ret+="\n";
		}
		return ret;
	}

	/**
	 * @return the width
	 */
	public int getWidth() {
		return width;
	}
	
	
	public Square[][] getSquareMap() {
		return squareMap;
	}
	
	
	public int getStateValue(int i , int j){
		return squareMap[i][j].getStateValue();
	}
	
//	public int getNextStateValue(int i , int j){
//		return squareMap[i][j].getNextStateValue();
//	}
	public void setStateMap(Square[][] stateMap) {
		this.squareMap = stateMap;
	}
	public Color getColor(int i,int j){
		return squareMap[i][j].getColor();
	}
	
	
	
	public void evolve(){
		
		System.out.println("tour :"+tour+ " nbLoup : "+nbWolf+ " nbSheep : "+nbSheep);
		tour++;
		for(Event e : eventList){
			//Apply event
			e.applyEvent(this);
		}
		toRepaint = new ArrayList<Square>();
		ArrayList<Rule> rules = getRules();
		for(int i = 0 ; i < width ; i ++){
			for(int j = 0 ; j < width ; j++){
				Rule currentRule = null;
				for(Rule r : rules){
					currentRule = r;
/*					
  					if(r.getRuleValue() == Rule.WATERFLOW1){
						if(!((WaterFlowRule1) r).isModify(i, j)){
							r.getEvolvedSquareMap()[i][j] = null;
						}
					}
					if(currentRule == null && r != null && r.getEvolvedSquareMap()[i][j] != null){
						currentRule = r;
					}
					else{
						if(r.getEvolvedSquareMap()[i][j] != null){
							switch (currentRule.getRuleValue()){
								case Rule.FIREPROPAGATION1 :
									switch(r.getRuleValue()){
										case Rule.FIREPROPAGATION1 :
											currentRule = r;
											break;
										case Rule.WATERFLOW1 :
//											if(((WaterFlowRule1) r).isModify(i, j)){
												currentRule = r;
//											}
											break;
										case Rule.NATURALEVOLUTION :
											break;
									}
									break;
								case Rule.WATERFLOW1 :
									
									switch(r.getRuleValue()){
										
										case Rule.FIREPROPAGATION1 :
//											if(!((WaterFlowRule1) currentRule).isModify(i, j)){
												currentRule = r;
//											}
											break;
										case Rule.WATERFLOW1 :
											currentRule = r;
											break;
										case Rule.NATURALEVOLUTION :
											break;
									}
									break;
								case Rule.NATURALEVOLUTION :
									switch(r.getRuleValue()){
										case Rule.NATURALEVOLUTION :
											currentRule = r;
											break;	
										case Rule.FIREPROPAGATION1 :
											break;
										case Rule.WATERFLOW1 :
											break;
										
									}
									break;
							}
						}
					}
					//*/
				}
					
				if(currentRule != null){
					if(!currentRule.getEvolvedSquareMap()[i][j].hasTheSameView(squareMap[i][j])){
						toRepaint.add(currentRule.getEvolvedSquareMap()[i][j]);
					}
					squareMap[i][j] = currentRule.getEvolvedSquareMap()[i][j];
				}
			}
		}
		for(int i = 0 ; i < agents.size() ;i++){
			Agent a = agents.get(i);
			a.step();
		}
		for(int i = 0 ; i < agents.size() ;i++){
			Agent a = agents.get(i);
			a.survive();
		}
	}
	
	
		
	
	public ArrayList<Square> getToRepaint() {
		return toRepaint;
	}


	public Square[][] cloneSquareMap(){
		Square[][] clone = new Square[width][width];
		for(int i = 0 ; i < width ; i++){
			for(int j = 0 ; j < width ; j++){
				clone[i][j] =(Square) squareMap[i][j].clone();
			}
		}
		return clone;
	}

	
	public ArrayList<Square> getEvovledSquares(int i, int j){
		ArrayList<Square> result = new ArrayList<Square>();
		for(Event e : eventList){
			for(Rule r : e.getRules()){
				result.add(r.getEvolvedSquareMap()[i][j]);
			}
		}
		return result;
	}
	
	
	public ArrayList<Rule> getRules(){
		ArrayList<Rule> result = new ArrayList<Rule>();
		for(Event e : eventList){
			for(Rule r : e.getRules()){
				result.add(r);
			}
		}
		return result;
	}

	public void refreshMap(){
		toRepaint = new ArrayList<Square>();
		for(int i = 0 ; i < width ; i++){
			for(int j = 0 ; j < width ; j++){
				toRepaint.add(squareMap[i][j]);
			}
		}
	}
	
	
	
	public static void main(String[] args){
		boolean[] a = new boolean[12];
		for(int i = 0 ; i < a.length ; i++){
			System.out.println(a[i]);
		}
	}
	
	
	public Square getSquare(int i, int j){
		return squareMap[i][j];
	}
	
	public void addAgent(Agent a){
		if(agents == null){
			agents = new ArrayList<Agent>();
		}
		if(agentsMap == null){
			agentsMap = new Agent[width][width];
		}
		
		agentsMap[a.getPosX()][a.getPosY()] = a;
		agents.add(a);
	}
	
	
	public void removeAgent(Agent a){
		if(agents == null){
			agents = new ArrayList<Agent>();
		}
		if(agentsMap == null){
			agentsMap = new Agent[width][width];
		}
		agents.remove(a);
		agentsMap[a.getPosX()][a.getPosY()] = null;
		if(a.getId() == Agent.SHEEP){
			nbSheep--;
		}
		else if(a.getId() == Agent.WOLF){
			nbWolf--;
		}
	}
	
	public Agent getAgent(int x , int y){
		return agentsMap[x][y];
	}
}
